#pragma ident "$Id: EpochDiffOp.cpp 2619 2012-04-11 03:36:15Z shjzhang $"

/**
 * @file EpochDiffOp.cpp
 * This is a class to apply the time difference Delta operator
 * to GNSS data structures.
 */

//============================================================================
//
//
//  If the satellite in current epoch, is not found in the previous epoch,
//  just jump and do nothing. ( Shoujian Zhang, 2012.04.19)
//
//  Shoujian Zhang - Wuhan University. 2011, 2012
//
//============================================================================


#include "EpochDiffOp.hpp"


namespace gpstk
{

      // Return a string identifying this object.
   std::string EpochDiffOp::getClassName() const
   { return "EpochDiffOp"; }



      /* Method to add a set of data value types to be differenced.
       *
       * @param diffSet       TypeIDSet of data values to be added to the
       *                      ones being differenced.
       */
   EpochDiffOp& EpochDiffOp::addTypeSet(const TypeIDSet& diffSet)
   {

         // Iterate over 'diffSet' and add its components to 'diffTypes'
      TypeIDSet::const_iterator pos;
      for (pos = diffSet.begin(); pos != diffSet.end(); ++pos)
      {
         diffTypes.insert(*pos);
      }

      return (*this);

   }  // End of method 'EpochDiffOp::addDiffTypeSet()'


   void EpochDiffOp::Process(gnssRinex& gData)
      noexcept(false)
   {

      try
      {
         Process(gData.body);

      }
      catch(Exception& u)
      {
             // Throw an exception if something unexpected happens
         ProcessingException e( getClassName() + u.what() );
         GPSTK_THROW(e);
      }
   }

      /* Return a reference to a satTypeValueMap object after differencing
       * data type values given in 'diffTypes' field with the previous
       * corresponding type.
       *
       * @param gData      Data object holding the data.
       */
   void EpochDiffOp::Process(commonHeader& comHeader,gnssRinex& gData)
      noexcept(false)
   {

      try
      {
         Process(gData.body);

      }
      catch(Exception& u)
      {
             // Throw an exception if something unexpected happens
         ProcessingException e( getClassName() + u.what() );
         GPSTK_THROW(e);
      }
   }


     /** Return a sourceRinex object, adding the new data generated
      *  when calling this object.
      *
      * @param gData    Data object holding the data.
      */
    void EpochDiffOp::Process(commonHeader& comHeader, sourceRinex& gData)
        noexcept(false)
    {
        for( sourceDataMap::iterator sdmIt = gData.body.begin();
             sdmIt != gData.body.end();
             ++sdmIt )
        {
            Process( sdmIt->second );
        }

    }  // End of method 'EpochDiffOp::Process()'


      /* Return a reference to a satTypeValueMap object after differencing
       * data type values given in 'diffTypes' field with the previous
       * corresponding type.
       *
       * @param gData      Data object holding the data.
       */
   satTypeValueMap& EpochDiffOp::Process(satTypeValueMap& gData)
      noexcept(false)
   {

      try
      {
         SatIDSet satRejectedSet;

            // Loop through all the satellites in the station data set
         satTypeValueMap::iterator it;
         for (it = gData.begin(); it != gData.end(); ++it)
         {

               // Let's find if the same satellite is present in gPrev
            satTypeValueMap::const_iterator itformer;

            itformer = gPrev.find((*it).first);

               // If we find the satellite, let's proceed with the differences
            if (itformer != gPrev.end())
            {
                  // Now, Let's check the satArc firstly. If the sat arc
                  // is not equal, remove the satellite directly.
               double satArc = gData((*it).first)(TypeID::satArc);
               double satArcPrev = gPrev((*it).first)(TypeID::satArc);

               if( satArc != satArcPrev )
               {
                     // If cycle slip happened, then schedule this
                     // satellite for removal
                  satRejectedSet.insert( (*it).first );

                     // Skip this value if problems arise
                  continue;
               }


                  // We must compute the difference for all the types in
                  // 'diffTypes' set
               TypeIDSet::const_iterator itType;
               for( itType  = diffTypes.begin();
                    itType != diffTypes.end();
                    ++itType )
               {
                  double value1(0.0);
                  double value2(0.0);

                  try
                  {
                        // Let's try to compute the difference
                     value1 = gData((*it).first)(*itType);
                     value2 = gPrev((*it).first)(*itType);

                        // result type
                     TypeID resultType;

                        // Get difference into data structure
                     if( (*itType) == (TypeID::prefitC1))
                     { 
                         resultType = TypeID::diffPrefitC1; 
                     }
                     else if( (*itType) == (TypeID::prefitC2))
                     { 
                         resultType = TypeID::diffPrefitC2; 
                     }
                     else if( (*itType) == (TypeID::prefitL1))
                     { 
                         resultType = TypeID::diffPrefitL1; 
                     }
                     else if( (*itType) == (TypeID::prefitL2))
                     { 
                         resultType = TypeID::diffPrefitL2; 
                     }
                     else if( (*itType) == (TypeID::prefitC12))
                     { 
                         resultType = TypeID::diffPrefitC12; 
                     }
                     else if( (*itType) == (TypeID::prefitL12))
                     { 
                         resultType = TypeID::diffPrefitL12; 
                     }
                     else if( (*itType) == (TypeID::wetMap))
                     { 
                         resultType = TypeID::diffWetMap; 
                     }
                     else
                     {
                        GPSTK_THROW(TypeIDNotFound("TypeID not found in diffTypes"));
                     }

                       // Insert the result type into gData
                     (*it).second[resultType] = value1 - value2;

                  }
                  catch(...)
                  {
                        // Skip this value if problems arise
                     continue;
                  }

               }  // End of 'for( itType = diffTypes.begin(); ...'

            }  // if( itformer)

         }  // End of 'for (it = gData.begin(); it != gData.end(); ++it)'

         gData.removeSatID(satRejectedSet);

            // Store the current gData
            // Warning: !!!!!!!!!
            // store the raw gData to 'gPrev'
         gPrev = gData;

         return gData;

      }
      catch(Exception& u)
      {
            // Throw an exception if something unexpected happens
         ProcessingException e( getClassName() + u.what() );

         GPSTK_THROW(e);

      }

   }  // End of method 'EpochDiffOp::Process()'


}  // End of namespace gpstk
